package com.you.raft.core.impl;

import com.you.raft.common.entity.LogEntry;
import com.you.raft.core.LogModule;
import lombok.extern.slf4j.Slf4j;

import java.util.Deque;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 日志模块:用Deque队列实现
 */
@Slf4j
public class DefaultLogModule implements LogModule {

    private final Map<Long, LogEntry> logEntryMap = new ConcurrentHashMap<>();
    private final Deque<LogEntry> logEntries = new ConcurrentLinkedDeque<>();
    /**
     * 默认初始化一个虚拟日志(索引为0),真正的日志索引从1开始，逐渐递增
     * 规定：不同机器的0号日志是一定相同的，同时，0号日志是不允许删除的
     */
    private final AtomicLong lastIndex = new AtomicLong(-1);
    private final ReentrantLock lock = new ReentrantLock();

    private static class DefaultLogModuleLazyHolder {
        private static final DefaultLogModule INSTANCE = new DefaultLogModule();

        static {
            LogEntry logEntry = new LogEntry();
            INSTANCE.write(logEntry);
        }

    }

    public static DefaultLogModule getInstance() {
        return DefaultLogModuleLazyHolder.INSTANCE;
    }

    @Override
    public void write(LogEntry logEntry) {
        try {
            if (!lock.tryLock(3000, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("尝试加锁异常");
            }
            long index = lastIndex.incrementAndGet();
            logEntry.setIndex(index);
            logEntries.addLast(logEntry);
            logEntryMap.put(index, logEntry);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public LogEntry getLast() {
        return logEntries.getLast();
    }

    @Override
    public long getLastIndex() {
        return lastIndex.get();
    }

    @Override
    public LogEntry get(long index) {
        return logEntryMap.get(index);
    }

    @Override
    public void removeStartWith(long index) {
        if (index <= 0) {
            return;
        }
        try {
            if (!lock.tryLock(3000, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("尝试加锁异常");
            }
            long idx = -1;
            while (!logEntries.isEmpty() && (idx = logEntries.getLast().getIndex()) >= index) {
                logEntries.removeLast();
                logEntryMap.remove(idx);
            }
            lastIndex.set(idx);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}
